/*
    This file is part of QTau
    Copyright (C) 2013-2018  Tobias "Tomoko" Platen <tplaten@posteo.de>
    Copyright (C) 2013       digited       <https://github.com/digited>
    Copyright (C) 2010-2013  HAL@ShurabaP  <https://github.com/haruneko>

    QTau is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    SPDX-License-Identifier: GPL-3.0+
*/

#include "midifile.h"
#include <smf.h>
#include <QJsonArray>
#include <QJsonObject>
#include "math.h"
#include "ustjkeys.h"
#include "tempomap.h"

static void importTempomap(const smf_t *smf,TempoMap* tmap)
{
    int i;
    smf_tempo_t *tempo;

    int current_measure = 0;
    int last_time = 0;
    int numerator = -1;
    int denominator = -1;
    int microseconds_per_quarter_note = 0;

    for (i = 0;; i++) {
        tempo = smf_get_tempo_by_number(smf, i);
        if (tempo == NULL)
            break;
        printf("------\n");

        //compute deltas
        int delta = tempo->time_pulses-last_time;
        last_time = tempo->time_pulses;
        int t = delta/tempo->numerator/smf->ppqn*tempo->denominator/4;
        //integrate
        current_measure += t;
        //output measure


        //output change events
        if(microseconds_per_quarter_note != tempo->microseconds_per_quarter_note)
        {
            float bpm = 60 * 1000 * 1000 / tempo->microseconds_per_quarter_note;
            printf("bpm %f\n",bpm);
            tmap->addTempo(current_measure,bpm);
        }

        if(numerator != tempo->numerator || denominator != tempo->denominator)
        {
            tmap->addTimeSignature(current_measure,tempo->numerator,tempo->denominator);
        }

        microseconds_per_quarter_note = tempo->microseconds_per_quarter_note;
        numerator = tempo->numerator;
        denominator = tempo->denominator;

    }
}

MidiFile::MidiFile() {}

int MidiFile::saveMidi(const QJsonArray &ust, QString fileName) {
    // FIXME: ustJson(ust);

    smf_t *smf;
    smf_track_t *track;
    smf_event_t *event;

    smf = smf_new();

    track = smf_track_new();
    smf_add_track(smf, track);

    int bpm = 60;
    // int bpm = getTempo();
    track = smf_track_new();
    smf_add_track(smf, track);
    char temposig[6];
    int tempo = 60 * 1000 * 1000 / bpm;
    temposig[0] = 0xFF;
    temposig[1] = 0x51;
    temposig[2] = 0x03;
    temposig[3] = (tempo >> 16) & 0xFF;
    temposig[4] = (tempo >> 8) & 0xFF;
    temposig[5] = (tempo)&0xFF;
    event = smf_event_new_from_pointer(temposig, 6);
    smf_track_add_event_pulses(track, event, 0);
    char timesig[7];
    timesig[0] = 0xFF;
    timesig[1] = 0x58;
    timesig[2] = 0x04;
    // QJsonArray a = getTimeSignature();
    // int nn=a[0].toInt();
    // int dd=a[1].toInt();
    int nn = 4;
    int dd = 4;
    dd = log(dd) / log(2);
    timesig[3] = nn;
    timesig[4] = dd;
    timesig[5] = 0;
    timesig[6] = 8;
    event = smf_event_new_from_pointer(timesig, 7);
    smf_track_add_event_pulses(track, event, 0);

    for (int i = 0; i < ust.count(); ++i) {
        auto o = ust[i];

        double ts = 0.25;

        unsigned char midi[3];
        midi[0] = 0x90;
        midi[1] = o.toObject()[NOTE_KEY_NUMBER].toInt();
        midi[2] = 100;  // FIXME do not hardcode

        int noteOffset = o.toObject()[NOTE_PULSE_OFFSET].toInt();
        int noteLength = o.toObject()[NOTE_PULSE_LENGTH].toInt();

        if (noteLength > 0) {
            event = smf_event_new_from_pointer(midi, 3);
            smf_track_add_event_pulses(track, event, ts * noteOffset);

            // http://www.ccarh.org/courses/253/handout/smf/

            QString lyric = o.toObject()[NOTE_LYRIC].toString();

            event = smf_event_new_textual(0x05, lyric.toUtf8());

            smf_track_add_event_pulses(track, event, ts * noteOffset);
            midi[0] = 0x80;
            event = smf_event_new_from_pointer(midi, 3);
            smf_track_add_event_pulses(track, event, ts * (noteOffset + noteLength));
        }
    }

    return smf_save(smf, fileName.toUtf8());
}

void MidiFile::loadMidi(QString fileName, QJsonArray &ustRef) {
    smf_t *smf;
    smf_event_t *event;
    smf = smf_load(fileName.toUtf8());

    if (smf == NULL) return;

    TempoMap tmap;
    importTempomap(smf,&tmap);

    QJsonObject setup;
    QJsonArray json;
    tmap.toJson(json);
    setup[TEMPOMAP] = json;
    setup[USER_AGENT] = "QTAU::MidiFile";
    ustRef.push_back(setup);

    smf_track_t *track = smf_get_track_by_number(smf, smf->number_of_tracks);
    int activeNote = -1;
    int notePos = 0;

    QString text;

    for (int i = 1; i < track->number_of_events + 1; i++) {
        event = smf_track_get_event_by_number(track, i);
        if (smf_event_is_textual(event)) {
            char *txt = smf_event_extract_text(event);
            text = txt;
            free(txt);
        } else {
            if (event->midi_buffer_length == 3) {
                unsigned char status = event->midi_buffer[0];
                unsigned char statusb = 0xF0 & status;
                unsigned char notenum = event->midi_buffer[1];
                unsigned char velocity = event->midi_buffer[2];

                float factor = 1;

                if (statusb == 0x80 || (statusb == 0x90 && velocity == 0)) {

                    int length = (event->time_pulses - notePos);
                    QJsonObject note;
                    note[NOTE_PULSE_OFFSET] = notePos * factor;
                    note[NOTE_PULSE_LENGTH] = length * factor;
                    note[NOTE_KEY_NUMBER] = activeNote;
                    note[NOTE_VELOCITY] = velocity;
                    if (text.length() == 0) text = "a";
                    note[NOTE_LYRIC] = text;
                    ustRef.append(note);
                    activeNote = -1;
                } else if (statusb == 0x90) {
                    if (activeNote != -1) {
                        smf_delete(smf);
                        return;
                    }
                    activeNote = notenum;
                    notePos = event->time_pulses;
                }
            }
        }
    }



    smf_delete(smf);
}
